#include "stdafx.h"
#include "BigInt.h"

using namespace System;
using namespace System::Text;
using namespace System::Collections::Generic;
using namespace Microsoft::VisualStudio::TestTools::UnitTesting;
using namespace APIAL;

namespace BigIntUnitTest
{
	[TestClass]
	public ref class SubtractUnitTest
	{
	private:
		TestContext^ testContextInstance;
		static BigInt *op1;
		static BigInt *op2;
		static BigInt *scratch1;
		static BigInt *result1;
		static BigInt *result2;
		static BigInt *expected;

	public: 
		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		property Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ TestContext
		{
			Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ get()
			{
				return testContextInstance;
			}
			System::Void set(Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ value)
			{
				testContextInstance = value;
			}
		};

		#pragma region Additional test attributes
		//
		//You can use the following additional attributes as you write your tests:
		//
		//Use ClassInitialize to run code before running the first test in the class
		[ClassInitialize()]
		static void MyClassInitialize(Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ testContext) {
			op1 = new BigInt(20, 8);
			op2 = new BigInt(20, 8);
			result1 = new BigInt(20, 8);
			result2 = new BigInt(20, 8);
			scratch1 = new BigInt(20, 8);
			expected = new BigInt(20, 8);
		}

		//Use ClassCleanup to run code after all tests in a class have run
		[ClassCleanup()]
		static void MyClassCleanup() { 
			delete op1;
			delete op2;
			delete result1;
			delete result2;
			delete scratch1;
			delete expected;
		}
		
		//Use TestInitialize to run code before running each test
		//[TestInitialize()]
		//void MyTestInitialize() {
		//	op1->clear();
		//	op2->clear();
		//}

		//
		//Use TestCleanup to run code after each test has run
		//[TestCleanup()]
		//void MyTestCleanup() {};
		//
		#pragma endregion 

		[TestMethod]
		void TestSubtractNoCheckNoBorrow()
		{
			op1->ParseHexadecimalString("FFFFFFFFFFFFFFFF", true);
			op2->ParseHexadecimalString("0123456789ABCDEF", true);
			BigInt::subtractNoCheck(op1, op2, result1, 0, op1->size);
			expected->ParseHexadecimalString("FEDCBA9876543210", true);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], result1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtractNoCheckBorrow()
		{
			op1->ParseBinaryString("1000000000000000000000", true);
			op2->ParseBinaryString("0111111111111111111111", true);
			BigInt::subtractNoCheck(op1, op2, result1, 0, op1->size);
			expected->ParseBinaryString("1", true);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], result1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtract1stOperandGreater()
		{
			op1->ParseHexadecimalString("235877ABC98", true);
			op2->ParseHexadecimalString("235876ABC98", true);
			Subtract(op1, op2, result1, scratch1);
			expected->ParseHexadecimalString("100000", true);
			Assert::IsTrue(result1->positive);
			Assert::AreEqual<int>(expected->size, result1->size);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], result1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtract2ndOperandGreater()
		{
			op1->ParseHexadecimalString("235876ABC98", true);
			op2->ParseHexadecimalString("235877ABC98", true);
			Subtract(op1, op2, result1, scratch1);
			expected->ParseHexadecimalString("100000", false);
			Assert::IsFalse(result1->positive);
			Assert::AreEqual<int>(expected->size, result1->size);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], result1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtractOperandsEqual()
		{
			op1->ParseHexadecimalString("235877ABC98", true);
			op2->ParseHexadecimalString("235877ABC98", true);
			Subtract(op1, op2, result1, scratch1);
			expected->ParseHexadecimalString("0", true);
			Assert::AreEqual<int>(expected->size, result1->size);
			Assert::AreEqual<int>(expected->size, 0);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], result1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtractRandom()
		{
			op1->ParseHexadecimalString("2398BCA23589BC", true);
			op2->ParseHexadecimalString("1876F395C87A32", true);
			Subtract(op1, op2, result1, scratch1);
			expected->ParseHexadecimalString("B21C90C6D0F8A", true);
			Assert::AreEqual<int>(expected->size, result1->size);
			Assert::IsTrue(result1->positive);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], result1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtract1stOperandLonger()
		{
			op1->ParseHexadecimalString("1298232359102380", true);
			op2->ParseHexadecimalString("FE98AC7698", true);
			Subtract(op1, op2, result1, scratch1);
			expected->ParseHexadecimalString("12982224C063ACE8", true);
			Assert::AreEqual<int>(expected->size, result1->size);
			Assert::IsTrue(result1->positive);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], result1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtractLeftResultAlias()
		{
			op1->ParseHexadecimalString("1298232359102380", true);
			op2->ParseHexadecimalString("FE98AC7698", true);
			Subtract(op1, op2, op1, scratch1);
			expected->ParseHexadecimalString("12982224C063ACE8", true);
			Assert::AreEqual<int>(expected->size, op1->size);
			Assert::IsTrue(op1->positive);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], op1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtractRightResultAlias()
		{
			op1->ParseHexadecimalString("1298232359102380", true);
			op2->ParseHexadecimalString("FE98AC7698", true);
			Subtract(op1, op2, op2, scratch1);
			expected->ParseHexadecimalString("12982224C063ACE8", true);
			Assert::AreEqual<int>(expected->size, op2->size);
			Assert::IsTrue(op2->positive);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], op2->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtractLeftRightResultAlias()
		{
			op1->ParseHexadecimalString("1298232359102380", true);
			Subtract(op1, op1, op1, scratch1);
			expected->ParseHexadecimalString("0", true);
			Assert::AreEqual<int>(expected->size, op1->size);
			for (int i = 0; i < expected->size; i++) {
				Assert::AreEqual<BigInt::chunkType>(expected->chunks[i], op1->chunks[i], 
					System::String::Format("Failed at index {0:d} out of {1:d}", i, expected->size));
			}
		};

		[TestMethod]
		void TestSubtractDifferingSigns() {
			op1->ParseHexadecimalString("235987235", true);
			op2->ParseHexadecimalString("93857BCE", false);
			expected->ParseHexadecimalString("2C91DEE03", true);

			// Test (left > right, pos-neg)
			Subtract(op1, op2, result1, scratch1);
			Assert::IsTrue(op1->positive);
			Assert::IsFalse(op2->positive);
			Assert::IsTrue(Equal(result1, expected));

			// Test (right > left, pos-neg)
			op2->positive = true;
			op1->positive = false;
			Subtract(op2, op1, result1, scratch1);
			Assert::IsTrue(op2->positive);
			Assert::IsFalse(op1->positive);
			Assert::IsTrue(Equal(result1, expected));

			// Test (left > right, neg-pos)
			op1->positive = false;
			op2->positive = true;
			expected->positive = false;
			Subtract(op1, op2, result1, scratch1);
			Assert::IsFalse(op1->positive);
			Assert::IsTrue(op2->positive);
			Assert::IsTrue(Equal(result1, expected));

			// Test (right > left, neg-pos)
			op2->positive = false;
			op1->positive = true;
			expected->positive = false;
			Subtract(op2, op1, result1, scratch1);
			Assert::IsFalse(op2->positive);
			Assert::IsTrue(op1->positive);
			Assert::IsTrue(Equal(result1, expected));

			// Test (left > right, pos-pos)
			op1->positive = true;
			op2->positive = true;
			expected->ParseHexadecimalString("1A212F667", true);
			Subtract(op1, op2, result1, scratch1);
			Assert::IsTrue(op1->positive);
			Assert::IsTrue(op2->positive);
			Assert::IsTrue(Equal(result1, expected));

			// Test (right > left, pos-pos)
			expected->positive = false;
			Subtract(op2, op1, result1, scratch1);
			Assert::IsTrue(op1->positive);
			Assert::IsTrue(op2->positive);
			Assert::IsTrue(Equal(result1, expected));

			// Test (left > right, neg-neg)
			op1->positive = false;
			op2->positive = false;
			expected->positive = false;
			Subtract(op1, op2, result1, scratch1);
			Assert::IsFalse(op1->positive);
			Assert::IsFalse(op2->positive);
			Assert::IsTrue(Equal(result1, expected));

			// Test (right > left, neg-neg)
			expected->positive = true;
			Subtract(op2, op1, result1, scratch1);
			Assert::IsFalse(op1->positive);
			Assert::IsFalse(op2->positive);
			Assert::IsTrue(Equal(result1, expected));

		}

		[TestMethod]
		void TestSubtractInsanelyHuge() {
			op1->ParseHexadecimalString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", true);
			op2->ParseHexadecimalString("A13B098677099790790879234525434CE234ABE4534432523523452345234BCEFFFFF234234", true);
			Subtract(op1, op2, result1, scratch1);
		}

	};
}
